#include "ipc_rv.h"
#include "ipc_rv_ex.h"
#include "gkt_config.h"

#if defined(GKT_CONFIG_FPS_ENABLE) && GKT_CONFIG_FPS_ENABLE

static void StartTick(void)
{
    StartTick_ex();
}

static G_Boolean Is_TimeOut(uint32_t TimeOut)
{
    return Is_TimeOut_ex(TimeOut);
}

static void PrintMsg(G_Message_TypeDef *Msg, const uint8_t *str)
{
    uint32_t cnt = 0;
    IPC_DEBUG_LOG(IPC_DEBUG_MSG, (MsgPRINT_DIR));
    IPC_DEBUG_LOG(IPC_DEBUG_MSG, ("%s:\n", str));
    IPC_DEBUG_LOG(IPC_DEBUG_MSG, ("%s Type:   %02x,0x%x\n",    str, Msg->type,&(Msg->type)));
    IPC_DEBUG_LOG(IPC_DEBUG_MSG, ("%s OPCode: %02x,0x%x\n", str, Msg->opcode,&(Msg->opcode)));
    IPC_DEBUG_LOG(IPC_DEBUG_MSG, ("%s Length: %04x,0x%x\n", str, Msg->DataLen,&(Msg->DataLen)));
    IPC_DEBUG_LOG(IPC_DEBUG_MSG, ("%s Data 0x%x:\n", str,Msg->pData));
    for (cnt = 0; cnt < Msg->DataLen; cnt++)
    {
        IPC_DEBUG_LOG(IPC_DEBUG_MSG, ("%02x ", Msg->pData[cnt]));
        if ((cnt % 16) == 15)
        {
            IPC_DEBUG_LOG(IPC_DEBUG_MSG, ("\n"));
        }
    }
    IPC_DEBUG_LOG(IPC_DEBUG_MSG, ("\n"));
}

static void IPC_RV_ASSERT(void)
{
    while (1);
}

static G_Boolean IPC_RV_InitChannel(G_pRingBuf_TyperDef *pIPC_CH, uint8_t *addr, IPC_RVBuf_Size_t size)
{
    *pIPC_CH = G_RingBufCreat(addr, size);
    if(NULL == pIPC_CH)
    {
        return FALSE;
    }

    return TRUE;
}

static G_Boolean IPC_RV_CH_Recv_Msg(G_pRingBuf_TyperDef pIPC_CH, G_Message_TypeDef *Msg, uint32_t TimeOut)
{
    StartTick();
    do
    {
        if (FALSE == RingBuf_Func.GetTry(pIPC_CH, (uint8_t *)Msg, MsgHEADLEN))
        {
            continue;
        }

        if (Msg->DataLen + MsgHEADLEN <= RingBuf_Func.GetDataLen(pIPC_CH))
        {
            RingBuf_Func.get(pIPC_CH, (uint8_t *)Msg, MsgHEADLEN);
            RingBuf_Func.get(pIPC_CH, Msg->pData, Msg->DataLen);
            return TRUE;
        }

        if (Msg->DataLen + MsgHEADLEN > pIPC_CH->BufSize)
        {
            IPC_DEBUG_LOG(IPC_DEBUG_ERROR, ("%s Msg ERROR %d [%d/%d]\r\n", MsgPRINT_DIR_EX, RingBuf_Func.GetDataLen(pIPC_CH), pIPC_CH->BufSize));
            IPC_RV_ASSERT();
        }
    }
    while (!Is_TimeOut(TimeOut));

    IPC_DEBUG_LOG(IPC_DEBUG_TIMEOUT_ERROR, ("%s Rx TimeOut [%d/%d]\r\n", MsgPRINT_DIR_EX, RingBuf_Func.GetDataLen(pIPC_CH), pIPC_CH->BufSize));
    return FALSE;
}

static G_Boolean IPC_RV_CH_Send_Msg(G_pRingBuf_TyperDef pIPC_CH, G_Message_TypeDef *Msg, uint32_t TimeOut)
{
    uint16_t len = MsgHEADLEN + Msg->DataLen;
    StartTick();
    do
    {
        if (RingBuf_Func.GetDataLen(pIPC_CH) + len <  pIPC_CH->BufSize)
        {
            RingBuf_Func.put(pIPC_CH, (uint8_t *)Msg, MsgHEADLEN);
            RingBuf_Func.put(pIPC_CH, Msg->pData, Msg->DataLen);
            G_IPC_Send_Trigger();
            return TRUE;
        }

    }
    while (!Is_TimeOut(TimeOut));

    IPC_DEBUG_LOG(IPC_DEBUG_TIMEOUT_ERROR, ("%s TX Full %d [%d/%d]\r\n", MsgPRINT_DIR_EX, len, RingBuf_Func.GetDataLen(pIPC_CH), pIPC_CH->BufSize));
    return FALSE;
}

G_Boolean G_IPC_RV_Init(uint8_t *baseAddr, IPC_RVBuf_Size_t size)
{
    uint8_t *addr = baseAddr;
    G_pIPC_RV_TypeDef p_IPC_RV = (G_pIPC_RV_TypeDef)(addr);

    if(size < sizeof(G_IPC_RV_TypeDef) + SyncChLEN * 2)
    {
        return FALSE;
    }

    addr += sizeof(G_IPC_RV_TypeDef);
    size -= sizeof(G_IPC_RV_TypeDef);

    if (FALSE == IPC_RV_InitChannel(&p_IPC_RV->Sync[M02RV],addr,SyncChLEN))
    {
        return FALSE;
    }

    addr += SyncChLEN;
    size -= SyncChLEN;

    if (FALSE == IPC_RV_InitChannel(&p_IPC_RV->Sync[RV2M0],addr,SyncChLEN))
    {
        return FALSE;
    }

    addr += SyncChLEN;
    size -= SyncChLEN;

    if (FALSE == IPC_RV_InitChannel(&p_IPC_RV->ASync[M02RV],addr,size/2))
    {
        return FALSE;
    }

    addr += size / 2;
    size -= size / 2;
    
    if((uint32_t)addr%4 != 0)
    {
        size -= 4 - (uint32_t)addr%4;
        addr += 4 - (uint32_t)addr%4;
    }

    if (FALSE == IPC_RV_InitChannel(&p_IPC_RV->ASync[RV2M0],addr,size))
    {
        return FALSE;
    }

    *(volatile uint32_t *)(pIPC_RVBaseAddr) = (uint32_t)p_IPC_RV;

    return TRUE;
}

G_Boolean G_IPC_RV_Recv_SyncMsg(G_IPC_RV_DIR_ENUM ch, G_Message_TypeDef *rMsg, uint32_t TimeOut)
{
    G_RingBuf_TyperDef *pIPC_CH = pIPC_RVCB->Sync[ch];
    if(TRUE != IPC_RV_CH_Recv_Msg(pIPC_CH, rMsg, TimeOut))
    {
        return FALSE;
    }

    MsgPRINT(rMsg, (uint8_t*)"rx Sync");

    return TRUE;
}

G_Boolean G_IPC_RV_Send_SyncMsg(G_IPC_RV_DIR_ENUM ch, G_Message_TypeDef *tMsg, G_Message_TypeDef *rMsg, uint32_t TimeOut)
{
    G_RingBuf_TyperDef *pIPC_CH = pIPC_RVCB->Sync[ch];

    if(TRUE != IPC_RV_CH_Send_Msg(pIPC_CH, tMsg, 0))
    {
        return FALSE;
    }

    MsgPRINT(tMsg, (uint8_t*)"tx Sync");
    G_IPC_Send_Trigger();

    /*don't recv*/
    if (NULL == rMsg)
    {
        return TRUE;
    }

    pIPC_CH = ch == M02RV ? pIPC_RVCB->Sync[RV2M0] : pIPC_RVCB->Sync[M02RV];
    if(TRUE != IPC_RV_CH_Recv_Msg(pIPC_CH, rMsg, TimeOut))
    {
        return FALSE;
    }

    MsgPRINT(rMsg, (uint8_t*)"rx Sync");

    return TRUE;
}

G_Boolean G_IPC_RV_Send_Msg(G_IPC_RV_DIR_ENUM ch, G_Message_TypeDef *tMsg, uint32_t TimeOut)
{
    G_RingBuf_TyperDef *pIPC_CH = pIPC_RVCB->ASync[ch];

    if(TRUE != IPC_RV_CH_Send_Msg(pIPC_CH, tMsg, 0))
    {
        return FALSE;
    }

    MsgPRINT(tMsg, (uint8_t*)"tx");
    G_IPC_Send_Trigger();

    return TRUE;
}

G_Boolean G_IPC_RV_Recv_Msg(G_IPC_RV_DIR_ENUM ch, G_Message_TypeDef *rMsg, uint32_t TimeOut)
{
    G_RingBuf_TyperDef *pIPC_CH = pIPC_RVCB->ASync[ch];
    if(TRUE != IPC_RV_CH_Recv_Msg(pIPC_CH, rMsg, TimeOut))
    {
        return FALSE;
    }

    MsgPRINT(rMsg, (uint8_t*)"rx");

    return TRUE;
}

#endif

